#include <iostream>
#include <functional>
#include <memory>
#include <thread>
#include <future>
#include <condition_variable>
#include <atomic>
#include <vector>

namespace pool 
{
class ThreadPool
{
    using Task = std::function<void()>;
public:
    ThreadPool() : isRunning_(false) {}
    ~ThreadPool() 
    {
       isRunning_ = false; 
    }
    void start(size_t initThreadSize = std::thread::hardware_concurrency())
    {
        isRunning_ = true;

        // 创建线程对象
        for(int i = 0; i < initThreadSize; ++i)
        {
            std::unique_ptr<std::thread> ptr = std::make_unique<std::thread>(
                std::bind(&ThreadPool::threadFunc, this));
            threads_.emplace_back(std::move(ptr));
            threads_[i]->detach();
        }
    }

    template<class Func, class ...Args>
    auto submitTask(Func&& func, Args&& ...args) -> std::future<decltype(func(args...))>        /* future<返回值>, 但Func表示的是整个函数接口的类型，*/
    {                                                                                           /* 因此需要引入decltype，传入函数对象及其参数推到出函数的返回值 */
        // 1. 推导返回值, 打包封装package任务
        using RType = decltype(func(args...));
        auto task = std::make_shared<std::packaged_task<RType()>>(              /* 通过make_shared管理函数对象，该指针的类型是一个函数对象 */
            std::bind(std::forward<Func>(func), std::forward<Args>(args)...)   /* 指向的是 func，其参数为 args... */
        );
        std::future<RType> result = task->get_future();

        // 2. 加锁push任务队列
        {
            std::unique_lock<std::mutex> lock(taskQueMtx_);
            taskpool_.emplace_back([task](){ (*task)(); });
            noEmpty_.notify_all();
        }
        return result;
    }

private:
    void threadFunc()
    {
        for(;;)
        {
            std::vector<Task> taskQue;
            {
                std::unique_lock<std::mutex> lock(taskQueMtx_);
                noEmpty_.wait(lock, [this](){ return taskpool_.size() > 0; });
                taskQue.swap(taskpool_);
            }
            for(auto& task : taskQue) task();
        }
    }
private:
    std::atomic_bool isRunning_;
    std::vector<Task> taskpool_;
    std::mutex taskQueMtx_;
    std::condition_variable noEmpty_;
    std::vector<std::unique_ptr<std::thread>> threads_;
};

using ThreadPoolPtr = std::shared_ptr<ThreadPool>;
}